package top.codedance.iotp.client.plugins.sync.can;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import top.codedance.iotp.client.ClientApplication;
import top.codedance.iotp.client.net.listenner.PluginService;
import top.codedance.iotp.client.plugins.sync.opc.OpcExecuterImpl;
import top.codedance.iotp.common.entity.PluginResultEntity;
import top.codedance.iotp.common.util.HexCodec;
import de.entropia.can.CanSocket;
import org.apache.log4j.Logger;
import org.ini4j.Ini;
import org.ini4j.Wini;

import java.sql.SQLException;

public class CanService implements PluginService, Runnable {
    private Logger logger = ClientApplication.getLogger(CanService.class);
    private static final String SERVICE_NAME = "CanService";
    private Integer clientId = 0;
    private String canName = "vcan0";
    private int mode = 0;
    private CanSocket socket;
    private CanSocket.CanInterface canIf;
    private Wini ini = null;

    public CanService(Integer clientId, Wini ini){
        this.clientId = clientId;
        this.ini = ini;
    }

    public static PluginService getInstance(Integer clientId, Wini ini) {
        PluginService pluginService = OpcExecuterImpl.getPluginService(SERVICE_NAME + clientId);
        if (null == pluginService) {
            CanService canService = new CanService(clientId, ini);
            OpcExecuterImpl.pluginServiceWorkerPool.submit(canService);
            pluginService = OpcExecuterImpl.registPluginService(SERVICE_NAME + clientId, canService);
        }
        return pluginService;
    }

    @Override
    public void run() {
        try {
            init();
        }catch (Exception e){
            logger.debug(e.getMessage());
        }
    }

    @Override
    public void init() throws Exception {
        try {
            if (ini != null && ini.containsKey("can")) {
                Ini.Section section = ini.get("can");
                if (section.containsKey("canName." + this.clientId)) {
                    canName = section.get("canName." + this.clientId);
                } else {
                    throw new Exception("parameter canName is empty.");
                }
                if (section.containsKey("mode." + this.clientId)) {
                    mode = Integer.valueOf(section.get("mode." + this.clientId)).intValue();
                } else {
                    throw new Exception("parameter mode is empty.");
                }
            } else {
                throw new Exception("can_service parameters is empty.");
            }
        } catch (ClassNotFoundException | SQLException e) {
            throw new Exception("can_service init parameters fail.");
        }
        CanSocket.Mode m;
        switch (mode){
            case 1:
                m = CanSocket.Mode.BCM;
                break;
            case 0:
            default:
                m = CanSocket.Mode.RAW;
                break;
        }
        socket = new CanSocket(m);
        canIf = new CanSocket.CanInterface(socket, canName);
        socket.bind(canIf);
    }

    @Override
    public PluginResultEntity execute(JSONArray items) {
        PluginResultEntity endResult = new PluginResultEntity();
        JSONArray outResult = new JSONArray();
        HexCodec hc = new HexCodec();
        JSONObject jo;
        byte[] data;
        for (int i = 0; i < items.size(); i++){
            try {
                jo = items.getJSONObject(i);
                String cmd = jo.getString("cmd");
                socket.send(new CanSocket.CanFrame(canIf, new CanSocket.CanId(jo.getIntValue("canId")), hc.toBytes(cmd)));
                CanSocket.CanFrame recv = socket.recv();
                data = recv.getData();
                JSONObject tmpJo = new JSONObject();
                tmpJo.put("tag", jo.getString("tag"));
                tmpJo.put("value", hc.toHex(data));
                tmpJo.put("ext", jo.getJSONObject("ext"));
                outResult.add(tmpJo);
            }catch (Exception e){
                logger.debug(e.getMessage());
            }
        }
        endResult.setContext(outResult);
        return endResult;
    }

    @Override
    public PluginResultEntity execute(JSONObject jo) {
        return new PluginResultEntity();
    }
}
